package fec

import (
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"errors"
	"log"
)

type FECScheme interface {
	GetRepairSymbols(fecGroup FECContainer, numberOfSymbols uint, id protocol.FECPayloadID) 	([]*RepairSymbol, error)
}

type BlockFECScheme interface {
	FECScheme
	RecoverPackets(fecGroup *FECGroup)		([][]byte, error)
	CanRecoverPackets(fecGroup *FECGroup)	bool
}

type ConvolutionalFECScheme interface {
	FECScheme
	CanRecoverPackets() bool
	RecoverPackets(protocol.FECPayloadID)		([][]byte, error)
	AddEquation(*Equation, []protocol.FECPayloadID, []*Variable)
	// returns true if the variable was an unknown of the current system (and thus could help to solve it), false otherwise
	AddKnownVariable(*Variable) bool
}

type XORFECScheme struct {
	IsConvolutional bool
}


var _ BlockFECScheme = &XORFECScheme{}

var XORFECSchemeCannotRecoverPacket = errors.New("XORFECScheme: cannot recover packet")
var XORFECSchemeCannotGetRepairSymbol = errors.New("XORFECScheme: cannot get repair symbol")
var XORFECSchemeTooMuchSymbolsNeeded = errors.New("XORFECScheme: cannot generate enough repair symbols")

func (s *XORFECScheme) CanRecoverPackets(group *FECGroup) bool {
	log.Printf("received %d packets, %d symbols", len(group.packetIndexes), len(group.RepairSymbols))
	return len(group.packetIndexes) == group.TotalNumberOfPackets- 1 && len(group.RepairSymbols) == 1
}

func (s *XORFECScheme) RecoverPackets(group *FECGroup) ([][]byte, error) {
	if !s.CanRecoverPackets(group) {
		return nil, XORFECSchemeCannotRecoverPacket
	}
	current := group.RepairSymbols[0].Data
	for _, i := range group.packetIndexes {
		current = XOR(current, group.packets[i])
	}
	return [][]byte{current}, nil
}

func (s *XORFECScheme) GetRepairSymbols(group FECContainer, numberOfSymbols uint, _ protocol.FECPayloadID) ([]*RepairSymbol, error) {
	packets := group.GetPackets()
	if len(packets) == 0 {
		return nil, XORFECSchemeCannotGetRepairSymbol
	}
	if numberOfSymbols > 1 {
		return nil, XORFECSchemeTooMuchSymbolsNeeded
	}
	var current []byte
	first := true
	for _, p := range packets {
		if first {
			current = p
			first = false
		} else {
			current = XOR(current, p)
		}
	}
	return []*RepairSymbol{{
		Data:            current,
		Convolutional:   s.Convolutional(),
	}}, nil
}

func (f *XORFECScheme) Convolutional() bool {
	return f.IsConvolutional
}

func XOR(a []byte, b []byte) []byte {
	var retVal []byte
	if len(a) >= len(b) {
		retVal = make([]byte, protocol.MaxReceivePacketSize)[:len(a)]
	} else {
		retVal = make([]byte, protocol.MaxReceivePacketSize)[:len(b)]
	}
	for i := 0 ; i < len(retVal) ; i++ {
		if i >= len(a) {
			retVal[i] = b[i]
		} else if i >= len(b) {
			retVal[i] = a[i]
		} else {
			retVal[i] = a[i] ^ b[i]
		}
	}
	return retVal
}